package com.jellyfishumbrella.GL;

import java.util.PriorityQueue;

public class AnimObject implements Cloneable {
	public enum CHANNEL {
		POS, ROT, SCALE
	}
	
	public enum ANIM_MODE{
		KEYFRAME,
		IMPULSE,
		DIRECT
	}

	public ANIM_MODE animMode = ANIM_MODE.KEYFRAME;
	
	public OctreeNode		octreeNode	= null;

	public boolean			isAnimating	= false;

	public KeyframeQueue	keyframes;
	private static long lastTime = 0;

	public AnimObject() {
		keyframes = new KeyframeQueue();
	}

	// ratio ranges from -1 to 1
	// -1 = keyframes.prevKey
	// 0 = keyframes.currKey
	// 1 = keyframes.nextKey
	//
	// clamp anything outside -1 to 1 range

	public AnimObject clone() {
		return new AnimObject();
	}

	public void animate(long worldTime) {
		switch (animMode){
			case KEYFRAME:
				animKeyframe(worldTime);
				break;
			case IMPULSE:
				animImpulse(worldTime);
				break;
			case DIRECT:
				animDirect(worldTime);
				break;
		}
	}
	
	public void animImpulse(long worldTime){
		long delta = worldTime - lastTime;
		lastTime = worldTime;
	}
	
	public void animDirect(long worldTime){
		long delta = worldTime - lastTime;
		lastTime = worldTime;
	}
	
	public void animKeyframe(long worldTime){
		synchronized (keyframes) {
			double ratio = 1;
			double A = 0;

			long animCurrTime = keyframes.currTime(worldTime);

			keyframes.defineMainKeyframes(worldTime);

			long prevKeyTime = ((Keyframe) keyframes.prevKey).time;
			long currKeyTime = ((Keyframe) keyframes.currKey).time;
			long nextKeyTime = ((Keyframe) keyframes.nextKey).time;

			if (nextKeyTime != currKeyTime) {
				A = nextKeyTime - currKeyTime;
				ratio = clamp((animCurrTime - currKeyTime) / A);
			}

			if (ratio == -1) {
				keyframes.add(keyframes.prevKey);
			}

			keyframes.here.pos = pos(ratio);
			keyframes.here.rot = rot(ratio);
			keyframes.here.scale = scale(ratio);

			if (octreeNode != null) {
				octreeNode.update(this);
			}
		}
	}

	public void goToKeyframe(Keyframe key) {
		keyframes.here.pos = key.pos;
		keyframes.here.rot = key.rot;
		keyframes.here.scale = key.scale;
	}

	public Vector3d pos(double ratio) {
		return getChannel(ratio, CHANNEL.POS);
	}

	public Vector3d rot(double ratio) {
		return getChannel(ratio, CHANNEL.ROT);
	}

	public Vector3d scale(double ratio) {
		return getChannel(ratio, CHANNEL.SCALE);
	}

	public Vector3d pos() {
		return ((Keyframe) keyframes.currKey).pos;
	}

	public Vector3d rot() {
		return ((Keyframe) keyframes.currKey).rot;
	}

	public Vector3d scale() {
		return ((Keyframe) keyframes.currKey).scale;
	}

	private Vector3d getChannel(double ratio, CHANNEL channel) {
		Vector3d newChannel = new Vector3d();
		Keyframe keyA;
		Vector3d channelA, channelB;

		clamp(ratio);
		if (ratio < 0) {
			keyA = (Keyframe) keyframes.prevKey;
		} else {
			keyA = (Keyframe) keyframes.nextKey;
		}

		switch (channel) {
			case POS:
				channelA = keyA.pos;
				channelB = ((Keyframe) keyframes.currKey).pos;
				break;
			case ROT:
				channelA = keyA.rot;
				channelB = ((Keyframe) keyframes.currKey).rot;
				break;
			case SCALE:
				channelA = keyA.scale;
				channelB = ((Keyframe) keyframes.currKey).scale;
				break;
			default:
				channelA = keyA.pos;
				channelB = ((Keyframe) keyframes.currKey).pos;
				break;
		}
		int i = 0;
		newChannel.sub(channelA, channelB);
		newChannel.scale(ratio);
		newChannel.add(channelB);

		return newChannel;
	}
	
	public void setPos(Vector3d newPos){
		keyframes.here.pos = newPos.clone();
	}
	
	public void setRot(Vector3d newRot){
		keyframes.here.rot = newRot.clone();
	}
	
	public void setScale(Vector3d newScale){
		keyframes.here.scale = newScale.clone();
	}

	private double clamp(double ratio) {
		if (ratio < -1) {
			return -1;
		} else if (ratio > 1) {
			return 1;
		} else {
			return ratio;
		}
	}

}
